/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.social.legacy.crypto.passwd.internal; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.params.KeyParameter; /** * Password-Based Key Derivation Function 2. * This is an implementation of the PBKDF2 which is defined as part of RSA's PKCS#5 * see: http://www.ietf.org/rfc/rfc2898.txt * * @since 2.5M1 * @version $Id: 76a30b90a727e56709310150f78254044700abd9 $ */ public class PBKDF2KeyDerivationFunction extends AbstractKeyDerivationFunction { /** * Fields in this class are set in stone! * Any changes may result in encrypted data becoming unreadable. * This class should be extended if any changes need to be made. */ private static final long serialVersionUID = 1L; /** Number of times to cycle the hash increasing processor expense. */ private int iterationCount; /** The length of the derived key (output) which should be produced. */ private int derivedKeyLength; /** A unique randomly generated byte array for frustrating attacks in bulk. */ private byte[] salt; /** A serializable representation of which digest to use. */ private final String digestClassName; /** * Internal State of functionF, this is only a class scoped variable to prevent the accumulation of * large amounts of garbage from the array being periodically allocated and dumped. */ private transient byte[] state; /** The message authentication code function to use. */ private transient Mac hMac; /** * Default Constructor. * Uses SHA-1 digest for compatabulity with PKCS#5 */ public PBKDF2KeyDerivationFunction() { this(new SHA1Digest()); } /** * Constructor with digest specified. * * @param hash the digest to use for the internal Hash-based Message Authentication Code function. */ public PBKDF2KeyDerivationFunction(Digest hash) { this.digestClassName = hash.getClass().getName(); this.hMac = new HMac(hash); this.state = new byte[this.hMac.getMacSize()]; } /** * {@inheritDoc} * * @see: org.xwiki.crypto.AbstractKeyDerivationFunction#init(byte[], int, int) */ @Override public void init(final byte[] salt, final int iterationCount, final int derivedKeyLength) { this.salt = salt; this.iterationCount = iterationCount; this.derivedKeyLength = derivedKeyLength; } @Override public synchronized byte[] deriveKey(byte[] password) { return generateDerivedKey(password, this.salt, this.iterationCount, this.derivedKeyLength); } /** * Generate the PBKDF2 derived key. * This is an implementation of PBKDF2(P, S, c, dkLen) defined in http://www.ietf.org/rfc/rfc2898.txt * * @param password the user supplied password expressed as a byte array. * @param salt the random salt to add to the password before hashing. * @param iterationCount the number of iterations which the internal function (F) should run. * @param derivedKeyLength the number of bytes of length the derived key should be (dkLen) * @return a byte array of length derivedKeyLength containing data derived from the password and salt. * suitable for a key. */ public synchronized byte[] generateDerivedKey(final byte[] password, final byte[] salt, final int iterationCount, final int derivedKeyLength) { // If this was deserialized, then the hmac must be loaded. if (this.hMac == null) { try { this.hMac = new HMac((Digest) Class.forName(this.digestClassName).newInstance()); this.state = new byte[this.hMac.getMacSize()]; } catch (Exception e) { throw new RuntimeException("Apparently this object was serialized when a digest (" + this.digestClassName + ") was available which is not available now.", e); } } try { int hLen = hMac.getMacSize(); // "Let l be the number of hLen-octet blocks in the derived key" (rfc2898) int numberOfBlocks = (derivedKeyLength + hLen - 1) / hLen; final byte[] currentIterationAsByteArray = new byte[4]; final byte[] key = new byte[numberOfBlocks * hLen]; for (int i = 1; i <= numberOfBlocks; i++) { this.integerToByteArray(i, currentIterationAsByteArray); this.functionF(password, salt, iterationCount, currentIterationAsByteArray, key, (i - 1) * hLen); } // Usually the key ends up being longer than the desired key length so it must be truncated byte[] out = new byte[derivedKeyLength]; System.arraycopy(key, 0, out, 0, derivedKeyLength); return out; } finally { // Set state to 0's in order to prevent the last state being read later. System.arraycopy(new byte[this.state.length], 0, this.state, 0, this.state.length); } } /** * Convert an integer to byte array in big-endian byte order. * <p> * Takes an int and an array of bytes. This array should be 4 bytes long. * Doesn't return anything in order to recycle the same memory locations.</p> * * @param integer the int which will be converted to an array of bytes. * @param outArray the array to populate with the output, this array should be 4 bytes. */ protected void integerToByteArray(int integer, byte[] outArray) { outArray[0] = (byte) (integer >>> 24); outArray[1] = (byte) (integer >>> 16); outArray[2] = (byte) (integer >>> 8); outArray[3] = (byte) integer; } /** * PBKDF#2 internal function F. * This is an implementation of F(P, S, c, l) defined in http://www.ietf.org/rfc/rfc2898.txt * * @param password (P) * @param salt (S) * @param iterationCount (c) * @param currentIteration when this function is called in a loop * this should be the current cycle in that loop. (l) * NOTE: to recycle memory, this parameter is given as a 4 byte array representing an int. * @param out the array which will be modified to contain the output. * @param outOffset the out array will be written to beginning at this index. */ protected synchronized void functionF(byte[] password, byte[] salt, int iterationCount, byte[] currentIteration, byte[] out, int outOffset) { CipherParameters passwordParam = new KeyParameter(password); this.hMac.init(passwordParam); if (salt != null) { this.hMac.update(salt, 0, salt.length); } this.hMac.update(currentIteration, 0, currentIteration.length); this.hMac.doFinal(this.state, 0); System.arraycopy(this.state, 0, out, outOffset, this.state.length); if (iterationCount < 1) { throw new IllegalArgumentException("iteration count must be at least 1."); } // i is initialized to 1 because the first cycle happened above. for (int i = 1; i < iterationCount; i++) { this.hMac.init(passwordParam); this.hMac.update(this.state, 0, this.state.length); this.hMac.doFinal(this.state, 0); // xor the current state against the output. // the output is never longer than state.length so this xor against the entire output. for (int j = 0; j < this.state.length; j++) { out[outOffset + j] ^= this.state[j]; } } } }